<script lang="ts">
import type { UseQueryReturnType } from '@peng_kai/kit/libs/vue-query';
import DefaultLoading from './Loading.vue';
import DefaultError from './Error.vue';
import DefaultEmpty from './Empty.vue';

export type TSvStatus = 'pending' | 'error' | 'success' | 'empty';

// #region 针对不同场景预设的 props 适配器
// use 前缀的返回值是响应式的；
// build 前缀的返回值是普通对象；

export function useSvPropsOfPromise(promise: Promise<any>) {
  const status = ref<TSvStatus>('pending');
  const msg = ref('');

  promise
    .then(() => {
      status.value = 'success';
    })
    .catch((err) => {
      status.value = 'error';
      msg.value = errToStr(err);
    });

  return reactive({ status, msg });
}

export function buildSvPropsOfQuery(
  query: UseQueryReturnType<any, any>,
  options?: { useFetching?: boolean; isEmptyFn?: (data: any) => boolean },
) {
  const { isEmptyFn, useFetching = false } = options ?? {};
  let status: TSvStatus = 'pending';
  const msg = errToStr(query.error.value);
  const onRetry = () => query.refetch();

  if (query.fetchStatus.value === 'fetching') {
    status = useFetching ? 'pending' : (query.isLoading.value ? 'pending' : 'success');
  }
  else if (query.fetchStatus.value === 'idle' && query.isSuccess.value) {
    const data = query.data.value;
    status = (isEmptyFn?.(data) ?? !query.data.value) ? 'empty' : 'success';
  }
  else {
    status = 'error';
  }

  return { status, msg, retryable: true, onRetry };
}
// #endregion

function errToStr(err: any) {
  if (err)
    return (typeof err?.toString === 'function') ? err.toString() : String(err);
  else
    return '';
}
</script>

<script setup lang="ts">
defineOptions({
  inheritAttrs: false,
});
const props = withDefaults(defineProps<{
  /** 状态 */
  status: TSvStatus;
  /** 信息 */
  msg?: string;
  /** 空状态的消息 */
  emptyMsg?: string;
  /** 是否可重试 */
  retryable?: boolean;
  /** 是否强制渲染内容，即使没有成功 */
  forceRender?: boolean;
  /** 是否将内容渲染到外部 */
  outRender?: boolean;
  nonSuccessClass?: string;
  loadingClass?: string;
  errorClass?: string;
  emptyClass?: string;
}>(), {
  forceRender: false,
  outRender: false,
  retryable: false,
});
const emits = defineEmits<{
  retry: [];
}>();

// 强制渲染 default slot 组件
const slots = defineSlots();
if (props.forceRender && slots?.default)
  slots.default({});
</script>

<template>
  <slot v-if="props.status === 'success' && props.outRender" />
  <div v-else v-bind="$attrs">
    <slot v-if="props.status === 'success' && !props.outRender" />
    <slot v-if="props.status === 'empty'" name="empty">
      <DefaultEmpty :class="[nonSuccessClass, emptyClass]" :msg="props.emptyMsg || props.msg" />
    </slot>
    <slot v-if="props.status === 'pending'" name="pending">
      <DefaultLoading :class="[nonSuccessClass, loadingClass]" />
    </slot>
    <slot v-if="props.status === 'error'" name="error">
      <DefaultError :class="[nonSuccessClass, errorClass]" :retryable="props.retryable" @retry="emits('retry')" />
    </slot>
  </div>
</template>
